////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001-2008.
// -------------------------------------------------------------------------
//  File name:   LocalMemoryUsage.cpp
//  Version:     v1.00
//  Created:     Istvan Kiss (Spidy) [11/5/2008]
//  Description: Visual helper for checking a local memory usage on maps
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

/*
	TODO:
		- Procedural vegetation

		- Permanent mip-map problem (streaming will collect a permanent mip-maps when travelling)
		- ? Foliage render nodes
		
		must make a decision about the local / global problem:
			- Particle effects (must check the calculation)
			- Characters (calculation is DONE)

		- StatObj LOD streaming (I think that there is no LOD streaming, but i'm not sure about this)
		- Terrain texture streaming (not detail textures!)	//It's not needed because this system using a fix-size pool
		- Animation streaming: completely different system, not need to measure now
		- IsoMesh streaming: completely different system, not need to measure now
*/


#include "StdAfx.h"
#if !defined(PS3) && !defined(XENON)
#include "LocalMemoryUsage.h"

#include <ITimer.h>
#include <IConsole.h>
#include <I3DEngine.h>
#include <IRenderAuxGeom.h>
#include <ICryAnimation.h>

//#define MAX_LODS 6										// I want an engine-wide const :-(
#define MAX_SLOTS 100									// GetSlotCount() is not working :-(
#define BIG_NUMBER 1e20f

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace LocalMemoryUsageDrawHelper
{
	enum eDrawRectParams
	{
		DSP_YDIR = 0,
		DSP_XDIR = 1,
		DSP_REVERSE_TRIANGLES = 2,
		DSP_REVERSE_ARROW = 4,
	};

	void DrawRect(IRenderer* pRenderer, const CCamera *camera, const Vec3 &p1, const Vec3 &p3, const ColorB &color, int params = 0 )
	{
		Vec3 p2, p4, projectedPos;
		if( !(params & DSP_REVERSE_TRIANGLES) )
		{
			if( params & DSP_XDIR )
			{
				p2 = Vec3(p1.x, p3.y, p1.z);
				p4 = Vec3(p3.x, p1.y, p3.z);
			}
			else
			{
				p2 = Vec3(p1.x, p3.y, p3.z);
				p4 = Vec3(p3.x, p1.y, p1.z);
			}
		}
		else
		{
			if( params & DSP_XDIR )
			{
				p4 = Vec3(p1.x, p3.y, p1.z);
				p2 = Vec3(p3.x, p1.y, p3.z);
			}
			else
			{
				p4 = Vec3(p1.x, p3.y, p3.z);
				p2 = Vec3(p3.x, p1.y, p1.z);
			}
		}

		if( camera->Project(p1, projectedPos) || camera->Project(p2, projectedPos) || camera->Project(p3, projectedPos) || camera->Project(p4, projectedPos) )
		{
			IRenderAuxGeom* pRenderAuxGeom = pRenderer->GetIRenderAuxGeom();

			pRenderAuxGeom->DrawTriangle( p1, color, p2, color, p3, color );
			pRenderAuxGeom->DrawTriangle( p3, color, p4, color, p1, color );
		}
	}

	void DrawArrow(IRenderer* pRenderer, const CCamera *camera, Vec3 pStart, Vec3 pEnd, float width, float head, float lengthSub, const ColorB &color, int params = 0 )
	{
		Vec3 p1, p2, p3, p4, p5, p6, p7, projectedPos;

		if( params & DSP_REVERSE_ARROW )
		{
			std::swap(pStart, pEnd);
		}

		Vec3 vParallel((pEnd-pStart).GetNormalized());
		Vec3 vOrto = vParallel.Cross(Vec3(0,0,1)).GetNormalized();
		pStart += vParallel * lengthSub;
		pEnd   -= vParallel * lengthSub;

		p1 = pStart - (vOrto * width * 0.5f);
		p2 = pStart + (vOrto * width * 0.5f);
		p3 = (pEnd - vParallel * head) - (vOrto * width * 0.5f);
		p4 = (pEnd - vParallel * head) + (vOrto * width * 0.5f);
		p5 = (pEnd - vParallel * head * 1.5f) - (vOrto * head * 0.9f);
		p6 = (pEnd - vParallel * head * 1.5f) + (vOrto * head * 0.9f);
		p7 = pEnd;

		if( camera->Project(p1, projectedPos) || camera->Project(p7, projectedPos) )
		{
			IRenderAuxGeom* pRenderAuxGeom = pRenderer->GetIRenderAuxGeom();

			pRenderAuxGeom->DrawTriangle( p1, color, p2, color, p4, color );
			pRenderAuxGeom->DrawTriangle( p4, color, p3, color, p1, color );
			pRenderAuxGeom->DrawTriangle( p3, color, p4, color, p7, color );
			pRenderAuxGeom->DrawTriangle( p4, color, p6, color, p7, color );
			pRenderAuxGeom->DrawTriangle( p5, color, p3, color, p7, color );
		}
	}

	#define DRAWHELPER_CIRCLE_POINT_NR 32

	
	static Vec3 s_CirclePoints[DRAWHELPER_CIRCLE_POINT_NR+1];
	static bool s_CircleDataInited = false;
	void InitCircleData()
	{
		s_CircleDataInited = true;
		float angleDiff = 2.0f*3.1415927f/DRAWHELPER_CIRCLE_POINT_NR;

		for(int i=0; i < DRAWHELPER_CIRCLE_POINT_NR+1; i++)
		{
			s_CirclePoints[i]=Vec3(cos(i*angleDiff), sin(i*angleDiff), 0.0f);
		}
	}

	void DrawCircle(IRenderer* pRenderer, const CCamera *camera, const Vec3 &origo, float radius, float ratio, const ColorB &color1, const ColorB &color2)
	{
		if( !s_CircleDataInited )
		{
			InitCircleData();
		}
		float radius2 = radius * 0.90f;

		IRenderAuxGeom* pRenderAuxGeom = pRenderer->GetIRenderAuxGeom();

		Vec3 oldPoint  = origo+s_CirclePoints[0]*radius;
		Vec3 oldPoint2 = origo+s_CirclePoints[0]*radius2;

		for( int i = 0; i < DRAWHELPER_CIRCLE_POINT_NR; i++ )
		{
			Vec3 newPoint  = origo+s_CirclePoints[i+1]*radius;
			Vec3 newPoint2 = origo+s_CirclePoints[i+1]*radius2;
			if( i < ratio*DRAWHELPER_CIRCLE_POINT_NR )
			{
				pRenderAuxGeom->DrawTriangle( origo, color1, oldPoint, color1, newPoint, color1 );
			}
			else
			{
				//pRenderAuxGeom->DrawTriangle( origo, color2, oldPoint, color2, newPoint, color2 );
				pRenderAuxGeom->DrawTriangle( oldPoint2, color2, oldPoint,  color2, newPoint,  color2 );
				pRenderAuxGeom->DrawTriangle( newPoint,  color2, newPoint2, color2, oldPoint2, color2 );
			}
			oldPoint  = newPoint;
			oldPoint2 = newPoint2;
		}
	}
}

/*
namespace Distance
{
	// Distance: AABB_AABB
	//----------------------------------------------------------------------------------
	// Calculate the closest distance of a AABB to an another AABB in 3d-space.
	// The function returns the squared distance. 
	// optionally the closest point on the hull is calculated
	//
	// Example:
	//  float result = Distance::Point_AABBSq( aabb1, aabb2 );
	//----------------------------------------------------------------------------------

	ILINE float AABB_AABBSq( const AABB& aabb1, const AABB& aabb2 ) 
	{	
		float fDist2 = 0;
		for(int i=0; i<3; ++i)
		{
			float max1 = aabb1.max[i];
			if(max1 < aabb2.min[i])
			{
				fDist2 += sqr(max1-aabb2.min[i]);
			}
			else 
			{
				float min1 = aabb1.min[i];
				if(min1 > aabb2.max[i])
				{
					fDist2 += sqr(aabb2.max[i]-min1);
				}
			}
		}
		return fDist2;
	}

	// Distance: AABB_AABB_2D
	//----------------------------------------------------------------------------------
	// Calculate the closest distance of a AABB to an another AABB in 2d-space.
	// The function returns the squared distance. 
	// optionally the closest point on the hull is calculated
	//
	// Example:
	//  float result = Distance::AABB_AABB2DSq( aabb1, aabb2 );
	//----------------------------------------------------------------------------------

	ILINE float AABB_AABB2DSq( const AABB& aabb1, const AABB& aabb2 ) 
	{	
		float fDist2 = 0;
		for(int i=0; i<2; ++i)						//We are using only 2 of the dimensions!
		{
			float max1 = aabb1.max[i];
			if(max1 < aabb2.min[i])
			{
				fDist2 += sqr(max1-aabb2.min[i]);
			}
			else 
			{
				float min1 = aabb1.min[i];
				if(min1 > aabb2.max[i])
				{
					fDist2 += sqr(aabb2.max[i]-min1);
				}
			}
		}
		return fDist2;
	}
}
*/

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int CLocalMemoryUsage::SResource::m_arrMemoryUsage[LOCALMEMORY_SECTOR_PER_PASS];
int CLocalMemoryUsage::SResource::m_arrPieces[LOCALMEMORY_SECTOR_PER_PASS];


CLocalMemoryUsage::SResource::SResource()
{
	m_used = true;
}

void CLocalMemoryUsage::SResource::Init(CLocalMemoryUsage *pLocalMemoryUsage)
{
	m_pLocalMemoryUsage = pLocalMemoryUsage;

	StartChecking();
}

void CLocalMemoryUsage::SResource::StartChecking()
{
	const RectI &actProcessedSectors = m_pLocalMemoryUsage->m_actProcessedSectors;

	float *pMipFactor = m_arrMipFactor;
	for( int i=0; i < actProcessedSectors.w*actProcessedSectors.h; i++)
	{
		*pMipFactor = BIG_NUMBER;
		pMipFactor++;
	}
	bool *pRowDirty = m_arrRowDirty;
	for( int i=0; i < LOCALMEMORY_SECTOR_PER_PASS; i++)
	{
		*pRowDirty = false;
		pRowDirty++;
	}
}

void CLocalMemoryUsage::SResource::CheckOnAllSectorsP1(const AABB &bounding, float maxViewDist, float scale, float mipFactor)
{
	//FRAME_PROFILER("! CLocalMemoryUsage::SResource::CheckOnAllSectorsP1", GetISystem(), PROFILE_SYSTEM);

	if(!m_pLocalMemoryUsage->m_pStreamCgfPredicitionDistance || maxViewDist < 0.01f)
		return;

	float e_StreamCgfPredicitionDistance = m_pLocalMemoryUsage->m_pStreamCgfPredicitionDistance->GetFVal();
	float modifiedmaxViewDist = maxViewDist+e_StreamCgfPredicitionDistance;
	float modifiedmaxViewDistSqr = sqr(modifiedmaxViewDist);

	const RectI &actProcessedSectors = m_pLocalMemoryUsage->m_actProcessedSectors;

	int xMin = (int)max(0,										 int((bounding.min.x - modifiedmaxViewDist) / LOCALMEMORY_SECTOR_SIZE    )-actProcessedSectors.x);
	int xMax = (int)min(actProcessedSectors.w, int((bounding.max.x + modifiedmaxViewDist) / LOCALMEMORY_SECTOR_SIZE + 1)-actProcessedSectors.x);
	int yMin = (int)max(0,										 int((bounding.min.y - modifiedmaxViewDist) / LOCALMEMORY_SECTOR_SIZE    )-actProcessedSectors.y);
	int yMax = (int)min(actProcessedSectors.h, int((bounding.max.y + modifiedmaxViewDist) / LOCALMEMORY_SECTOR_SIZE + 1)-actProcessedSectors.y);

	AABB sectorBounding(AABB::RESET);
	float sectorDistanceSqr;
	float fDistYSqr;

	for( int y=yMin; y<yMax; y++ )
	{
		float *pMipFactor = &m_arrMipFactor[actProcessedSectors.w * y + xMin];

		sectorBounding.min.y = (y+actProcessedSectors.y)*LOCALMEMORY_SECTOR_SIZE;
		sectorBounding.max.y = sectorBounding.min.y+LOCALMEMORY_SECTOR_SIZE;
		sectorBounding.min.x = (xMin+actProcessedSectors.x)*LOCALMEMORY_SECTOR_SIZE;
		
		assert( y < LOCALMEMORY_SECTOR_PER_PASS );
		m_arrRowDirty[y] = true;
		
		//Calculating Y distance sqr
		{
			fDistYSqr = 0;
			if(bounding.max.y < sectorBounding.min.y)
			{
				fDistYSqr += sqr(bounding.max.y-sectorBounding.min.y);
			}
			else 
			{
				if(bounding.min.y > sectorBounding.max.y)
				{
					fDistYSqr += sqr(sectorBounding.max.y-bounding.min.y);
				}
			}
		}

		for( int x=xMin; x<xMax; x++ )
		{
			sectorBounding.max.x = sectorBounding.min.x+LOCALMEMORY_SECTOR_SIZE;

			//sectorDistanceSqr = Distance::AABB_AABB2DSq(bounding, sectorBounding);

			//Adding X distance sqr
			{
				sectorDistanceSqr = fDistYSqr;
				if(bounding.max.x < sectorBounding.min.x)
				{
					sectorDistanceSqr += sqr(bounding.max.x-sectorBounding.min.x);
				}
				else 
				{
					if(bounding.min.x > sectorBounding.max.x)
					{
						sectorDistanceSqr += sqr(sectorBounding.max.x-bounding.min.x);
					}
				}
			}

			if(sectorDistanceSqr < modifiedmaxViewDistSqr)
			{
				*pMipFactor = min( *pMipFactor, sectorDistanceSqr*scale*scale*mipFactor );
			}

			pMipFactor++;

			sectorBounding.min.x = sectorBounding.max.x;
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CLocalMemoryUsage::STextureInfo::STextureInfo()
{
	m_pTexture = NULL;

	//m_size = 0;
	//m_xSize = 0;
	//m_ySize = 0;
	//m_numMips = 0;
}

CLocalMemoryUsage::STextureInfo::~STextureInfo()
{
	if( m_pTexture )
	{
		m_pTexture->Release();
	}
}

void CLocalMemoryUsage::STextureInfo::CheckOnAllSectorsP2()
{
	int x, y, sgnX, sgnY, nMip, nStreamableMipNr, memoryUsage, xDiff, yDiff, xDiffPieces, yDiffPieces, xOldMemoryUsage, xOldPieces;
	bool nonZero;
	ITexture *pTexture;
	
	const RectI &actProcessedSectors = m_pLocalMemoryUsage->m_actProcessedSectors;
	bool calculateDiff = (m_pLocalMemoryUsage->m_pDebugDraw->GetIVal()==18);

	bool wasDirty = false;
	for( y=0; y<actProcessedSectors.h; y++ )
	{
		int *pMemoryUsage = m_arrMemoryUsage;
		int *pPieces = m_arrPieces;
		if(m_arrRowDirty[y] || (calculateDiff && wasDirty))
		{
			SSector *sector = &m_pLocalMemoryUsage->m_arrSectors[(y + actProcessedSectors.y) * LOCALMEMORY_SECTOR_NR_X + actProcessedSectors.x];
			float *pMipFactor = &m_arrMipFactor[y * actProcessedSectors.w ];
			xOldMemoryUsage = 0;
			xOldPieces = 0;
			for( x=0; x<actProcessedSectors.w; x++ )
			{
				nonZero = (*pMipFactor != BIG_NUMBER);
				if( nonZero || (calculateDiff && (xOldMemoryUsage != 0 || xOldPieces != 0 || *pMemoryUsage != 0 || *pPieces != 0)))		// One more row
				{
					pTexture = m_pTexture;
					if( !nonZero )
					{
						memoryUsage = 0;
						nStreamableMipNr = 0;
					}
					else
					{
						nMip = max(0, pTexture->StreamCalculateMipsSigned(*pMipFactor));
						memoryUsage = pTexture->GetStreamableMemoryUsage(nMip);
						nStreamableMipNr = pTexture->GetStreamableMipNumber() - nMip;
					}

					sector->m_memoryUsage_Textures += memoryUsage;

					if( calculateDiff )
					{
						sgnX = sgn(x);
						sgnY = sgn(y);
						xDiff = memoryUsage - xOldMemoryUsage;
						yDiff = memoryUsage - *pMemoryUsage;
						xDiffPieces = nStreamableMipNr - xOldPieces;
						yDiffPieces = nStreamableMipNr - *pPieces;

						sector->m_memoryUsage_TexturesChangeXpos += max(xDiff, 0) * sgnX;
						sector->m_memoryUsage_TexturesChangeXneg -= min(xDiff, 0) * sgnX;
						sector->m_memoryUsage_TexturesChangeYpos += max(yDiff, 0) * sgnY;
						sector->m_memoryUsage_TexturesChangeYneg -= min(yDiff, 0) * sgnY;

						sector->m_memoryUsage_TexturePiecesChangeXpos += max(xDiffPieces, 0) * sgnX;
						sector->m_memoryUsage_TexturePiecesChangeXneg -= min(xDiffPieces, 0) * sgnX;
						sector->m_memoryUsage_TexturePiecesChangeYpos += max(yDiffPieces, 0) * sgnY;
						sector->m_memoryUsage_TexturePiecesChangeYneg -= min(yDiffPieces, 0) * sgnY;
					}

					xOldMemoryUsage = memoryUsage;
					*pMemoryUsage = memoryUsage;
					xOldPieces = nStreamableMipNr;
					*pPieces = nStreamableMipNr;
				}
				else
				{
					xOldMemoryUsage = 0;
					*pMemoryUsage = 0;
					xOldPieces = 0;
					*pPieces = 0;
				}
				sector++;
				pMipFactor++;
				pMemoryUsage++;
				pPieces++;
			}
			wasDirty = m_arrRowDirty[y];
		}
		else
		{
			for( x=0; x<actProcessedSectors.w; x++ )
			{
				*pMemoryUsage = 0;
				pMemoryUsage++;
				*pPieces = 0;
				pPieces++;
			}
			wasDirty = false;
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CLocalMemoryUsage::SMaterialInfo::SMaterialInfo()
{
}

void CLocalMemoryUsage::SMaterialInfo::AddTextureInfo(STextureInfoAndTilingFactor textureInfo)
{
	m_textures.push_back(textureInfo);
}

void CLocalMemoryUsage::SMaterialInfo::CheckOnAllSectorsP2()
{
	int x, y;
	
	const RectI &actProcessedSectors = m_pLocalMemoryUsage->m_actProcessedSectors;

	for( y=0; y<actProcessedSectors.h; y++ )
	{
		if(m_arrRowDirty[y])
		{
			float *pMipFactor = &m_arrMipFactor[y * actProcessedSectors.w ];
			for( x=0; x<actProcessedSectors.w; x++ )
			{
				if( *pMipFactor != BIG_NUMBER )
				{
					//(Spidy) Send the material MinDistance values to textures
					for( TTextureVector::iterator it = m_textures.begin(); it != m_textures.end(); ++it )
					{
						STextureInfoAndTilingFactor &textureInfo = *it;
						textureInfo.m_pTextureInfo->m_arrRowDirty[y] = true;

						float &textureMipFactor = textureInfo.m_pTextureInfo->m_arrMipFactor[actProcessedSectors.w * y + x];
						textureMipFactor = min(textureMipFactor, *pMipFactor*textureInfo.m_tilingFactor);
					}
				}
				pMipFactor++;
			}
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CLocalMemoryUsage::SStatObjInfo::SStatObjInfo()
{
	m_streamableContentMemoryUsage = 0;

	m_bSubObject = false;
/*
	m_lodNr = 0;
	m_vertices = 0;
	m_indices = 0;
	m_meshSize = 0;
	m_physProxySize = 0;
	m_physPrimitives = 0;
	for( int i=0; i < MAX_LODS; i++)
	{
		m_indicesPerLod[i]=0;
	}
*/
}

void CLocalMemoryUsage::SStatObjInfo::CheckOnAllSectorsP2()
{
	int x, y, sgnX, sgnY, memoryUsage, pieces, xDiff, yDiff, xDiffPieces, yDiffPieces, xOldMemoryUsage, xOldPieces;
	bool nonZero;

	const RectI &actProcessedSectors = m_pLocalMemoryUsage->m_actProcessedSectors;
	bool calculateDiff = (m_pLocalMemoryUsage->m_pDebugDraw->GetIVal()==18);

	bool wasDirty = false;
	for( y=0; y<actProcessedSectors.h; y++ )
	{
		int *pMemoryUsage = m_arrMemoryUsage;
		int *pPieces = m_arrPieces;
		if(m_arrRowDirty[y] || (calculateDiff && wasDirty))
		{
			SSector *sector = &m_pLocalMemoryUsage->m_arrSectors[(y + actProcessedSectors.y) * LOCALMEMORY_SECTOR_NR_X + actProcessedSectors.x];
			float *pMipFactor = &m_arrMipFactor[y * actProcessedSectors.w ];
			xOldMemoryUsage = 0;
			xOldPieces = 0;
			for( x=0; x<actProcessedSectors.w; x++ )
			{
				nonZero = (*pMipFactor != BIG_NUMBER);
				if( nonZero || ( calculateDiff && (xOldMemoryUsage != 0 || xOldPieces != 0 || *pMemoryUsage != 0 || *pPieces != 0)))		// One more row
				{
/*
					memoryUsage = 0;
					float minDistance = cry_sqrtf(minDistanceSqr);
					CStatObj* pStatObj = (CStatObj*)(m_pStatObj.get());					//(Spidy) Huh...

					//Iterate on all subobjects, like CStatObj::UpdateStreamableComponents
					float fSubObjectScale = 1.0f;
					//float fSubObjectScale = matSubObject.GetColumn0().GetLength();	//TODO
					int lod = pStatObj->GetLodFromScale(1.0f, fSubObjectScale, minDistance, false);
					//(Spidy) az objMatrix scale es a fLodRatioNorm mar bele vannak szamolva a minDistance-ba!
					memoryUsage = m_streamableContentMemoryUsage;
*/
					memoryUsage = m_streamableContentMemoryUsage;
					pieces = 1;

					if( !nonZero )
					{
						memoryUsage = 0;
						pieces = 0;
					}

					sector->m_memoryUsage_Geometry += memoryUsage;

					if( m_pLocalMemoryUsage->m_pDebugDraw->GetIVal()==18 )
					{
						sgnX = sgn(x);
						sgnY = sgn(y);
						xDiff = memoryUsage - xOldMemoryUsage;
						yDiff = memoryUsage - *pMemoryUsage;
						xDiffPieces = pieces - xOldPieces;
						yDiffPieces = pieces - *pPieces;

						sector->m_memoryUsage_GeometryChangeXpos += max(xDiff, 0) * sgnX;
						sector->m_memoryUsage_GeometryChangeXneg -= min(xDiff, 0) * sgnX;
						sector->m_memoryUsage_GeometryChangeYpos += max(yDiff, 0) * sgnY;
						sector->m_memoryUsage_GeometryChangeYneg -= min(yDiff, 0) * sgnY;

						sector->m_memoryUsage_GeometryPiecesChangeXpos += max(xDiffPieces, 0) * sgnX;
						sector->m_memoryUsage_GeometryPiecesChangeXneg -= min(xDiffPieces, 0) * sgnX;
						sector->m_memoryUsage_GeometryPiecesChangeYpos += max(yDiffPieces, 0) * sgnY;
						sector->m_memoryUsage_GeometryPiecesChangeYneg -= min(yDiffPieces, 0) * sgnY;
					}

					xOldMemoryUsage = memoryUsage;
					*pMemoryUsage = memoryUsage;
					xOldPieces = pieces;
					*pPieces = pieces;
				}
				else
				{
					xOldMemoryUsage = 0;
					*pMemoryUsage = 0;
					xOldPieces = 0;
					*pPieces = 0;
				}
				sector++;
				pMipFactor++;
				pMemoryUsage++;
				pPieces++;
			}
			wasDirty = m_arrRowDirty[y];
		}
		else
		{
			for( x=0; x<actProcessedSectors.w; x++ )
			{
				*pMemoryUsage = 0;
				pMemoryUsage++;
				*pPieces = 0;
				pPieces++;
			}
			wasDirty = false;
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CLocalMemoryUsage::SSector::SSector()
{
	StartChecking();
}

void CLocalMemoryUsage::SSector::StartChecking()
{
	m_memoryUsage_Textures = 0;
	m_memoryUsage_Geometry = 0;
	
	m_memoryUsage_TexturesChangeXpos = 0;
	m_memoryUsage_TexturesChangeXneg = 0;
	m_memoryUsage_TexturesChangeYpos = 0;
	m_memoryUsage_TexturesChangeYneg = 0;

	m_memoryUsage_TexturePiecesChangeXpos = 0;
	m_memoryUsage_TexturePiecesChangeXneg = 0;
	m_memoryUsage_TexturePiecesChangeYpos = 0;
	m_memoryUsage_TexturePiecesChangeYneg = 0;

	m_memoryUsage_GeometryChangeXpos = 0;
	m_memoryUsage_GeometryChangeXneg = 0;
	m_memoryUsage_GeometryChangeYpos = 0;
	m_memoryUsage_GeometryChangeYneg = 0;

	m_memoryUsage_GeometryPiecesChangeXpos = 0;
	m_memoryUsage_GeometryPiecesChangeXneg = 0;
	m_memoryUsage_GeometryPiecesChangeYpos = 0;
	m_memoryUsage_GeometryPiecesChangeYneg = 0;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CLocalMemoryUsage::CLocalMemoryUsage()
{
	REGISTER_CVAR(sys_LocalMemoryTextureLimit,								 64, VF_NULL, "LocalMemoryUsage tool: Set the texture memory limit to check streaming (Mb/sec)");
	REGISTER_CVAR(sys_LocalMemoryGeometryLimit,								 32, VF_NULL, "LocalMemoryUsage tool: Set the statobj geometry memory limit to check streaming (Mb/sec)");
	REGISTER_CVAR(sys_LocalMemoryTextureStreamingSpeedLimit,	 10, VF_NULL, "LocalMemoryUsage tool: Texture streaming speed limit (approx, Mb/sec)");
	REGISTER_CVAR(sys_LocalMemoryGeometryStreamingSpeedLimit,	 10, VF_NULL, "LocalMemoryUsage tool: Stat object geometry streaming speed limit (approx, Mb/sec)");

	REGISTER_CVAR(sys_LocalMemoryWarningRatio,							 0.8f, VF_NULL, "LocalMemoryUsage tool: Warning ratio for streaming");
	REGISTER_CVAR(sys_LocalMemoryOuterViewDistance,					500.f, VF_NULL, "LocalMemoryUsage tool: View distance for debug draw");
	REGISTER_CVAR(sys_LocalMemoryInnerViewDistance,					100.f, VF_NULL, "LocalMemoryUsage tool: View distance for detailed debug draw");

	REGISTER_CVAR(sys_LocalMemoryStreamingSpeedObjectLength, 10.f, VF_NULL, "LocalMemoryUsage tool: Length of the streaming speed debug object");
	REGISTER_CVAR(sys_LocalMemoryStreamingSpeedObjectWidth,	 1.5f, VF_NULL, "LocalMemoryUsage tool: Width of the streaming speed debug object");
	REGISTER_CVAR(sys_LocalMemoryObjectWidth,									6.f, VF_NULL, "LocalMemoryUsage tool: Width of the streaming buffer debug object");
	REGISTER_CVAR(sys_LocalMemoryObjectHeight,								2.f, VF_NULL, "LocalMemoryUsage tool: Height of the streaming buffer debug object");
	REGISTER_CVAR(sys_LocalMemoryObjectAlpha,									128, VF_NULL, "LocalMemoryUsage tool: Color alpha of the debug objects");

	REGISTER_CVAR(sys_LocalMemoryDiagramWidth,							 0.5f, VF_NULL, "LocalMemoryUsage tool: Width of the diagrams OBSOLATE");

	REGISTER_CVAR(sys_LocalMemoryDiagramRadius,							 2.5f, VF_NULL, "LocalMemoryUsage tool: Radius of the diagram");
	REGISTER_CVAR(sys_LocalMemoryDiagramDistance,						-5.5f, VF_NULL, "LocalMemoryUsage tool: Distance of the diagram from the main debug object");
	REGISTER_CVAR(sys_LocalMemoryDiagramStreamingSpeedRadius,		 1.0f, VF_NULL, "LocalMemoryUsage tool: Radius of the streaming speed diagram");
	REGISTER_CVAR(sys_LocalMemoryDiagramStreamingSpeedDistance,	 2.2f, VF_NULL, "LocalMemoryUsage tool: Distance of the streaming speed diagram from the streaming speed debug line");
	REGISTER_CVAR(sys_LocalMemoryDiagramAlpha,								255, VF_NULL, "LocalMemoryUsage tool: Color alpha of the diagrams");

	REGISTER_CVAR(sys_LocalMemoryDrawText,										  0, VF_NULL, "LocalMemoryUsage tool: If != 0, it will draw the numeric values");
	REGISTER_CVAR(sys_LocalMemoryLogText,												0, VF_NULL, "LocalMemoryUsage tool: If != 0, it will log the numeric values");

	REGISTER_CVAR(sys_LocalMemoryOptimalMSecPerSec,						100, VF_NULL, "LocalMemoryUsage tool: Optimal calculation time (MSec) per secundum");
	REGISTER_CVAR(sys_LocalMemoryMaxMSecBetweenCalls,				 1000, VF_NULL, "LocalMemoryUsage tool: Maximal time difference (MSec) between calls");
	
	m_pStreamCgfPredicitionDistance = 0;
	m_pDebugDraw = 0;
	if (gEnv->pConsole)
	{
		m_pStreamCgfPredicitionDistance = gEnv->pConsole->GetCVar("e_StreamCgfPredicitionDistance");
		m_pDebugDraw = gEnv->pConsole->GetCVar("e_DebugDraw");
	}

	m_sectorNr.zero();

	m_actProcessedSectors = RectI(0,0,0,0);
	m_actDrawedSectors = RectI(0,0,0,0);

	m_AverageUpdateTime = 0.01f;

	m_sectorNr.x = LOCALMEMORY_SECTOR_NR_X;				//TODO Check size of the world
	m_sectorNr.y = LOCALMEMORY_SECTOR_NR_Y;
}

CLocalMemoryUsage::~CLocalMemoryUsage()
{
	DeleteGlobalData();
}

void CLocalMemoryUsage::DeleteGlobalData()
{
	m_globalTextures.clear();
	m_globalMaterials.clear();
	m_globalStatObjs.clear();
}

void CLocalMemoryUsage::OnRender(IRenderer* pRenderer, const CCamera *camera)
{
	if( !m_pDebugDraw || (m_pDebugDraw->GetIVal()!=17 && m_pDebugDraw->GetIVal()!=18) )
		return;

	const Matrix34 &mat = camera->GetMatrix();
	Vec3 pos;
	Vec3 projectedPos;
	SSector *sector;

	SAuxGeomRenderFlags auxGeomRenderFlagsClose(e_Mode3D | e_AlphaBlended | e_DrawInFrontOn | e_FillModeSolid | e_CullModeBack | e_DepthWriteOff | e_DepthTestOff);
	SAuxGeomRenderFlags auxGeomRenderFlagsFar(e_Mode3D | e_AlphaBlended | e_DrawInFrontOff | e_FillModeSolid | e_CullModeBack | e_DepthWriteOff | e_DepthTestOn);

	IRenderAuxGeom* pRenderAuxGeom = pRenderer->GetIRenderAuxGeom();

	Vec3 cameraPos = mat.GetTranslation();

	int xMin = max(0,														int((cameraPos.x - sys_LocalMemoryOuterViewDistance) / LOCALMEMORY_SECTOR_SIZE));
	int xMax = min(LOCALMEMORY_SECTOR_PER_PASS - 1, int((cameraPos.x + sys_LocalMemoryOuterViewDistance) / LOCALMEMORY_SECTOR_SIZE + 1));
	int yMin = max(0,														int((cameraPos.y - sys_LocalMemoryOuterViewDistance) / LOCALMEMORY_SECTOR_SIZE));
	int yMax = min(LOCALMEMORY_SECTOR_PER_PASS - 1, int((cameraPos.y + sys_LocalMemoryOuterViewDistance) / LOCALMEMORY_SECTOR_SIZE + 1));
	m_actDrawedSectors.x = xMin;
	m_actDrawedSectors.y = yMin;
	m_actDrawedSectors.w = xMax-xMin+1;
	m_actDrawedSectors.h = yMax-yMin+1;

	CTimeValue actTime = gEnv->pTimer->GetAsyncTime();
	float timeSin = cry_sinf(actTime.GetSeconds() * 10.0f)*0.5f+0.5f;

	ColorB color;
	ColorB colorOK(LOCALMEMORY_COLOR_OK, sys_LocalMemoryObjectAlpha);
	ColorB colorWarning(LOCALMEMORY_COLOR_WARNING, sys_LocalMemoryObjectAlpha);
	ColorB colorError(LOCALMEMORY_COLOR_ERROR, uint8(sys_LocalMemoryObjectAlpha*timeSin));
	ColorB colorTexture(LOCALMEMORY_COLOR_TEXTURE, sys_LocalMemoryDiagramAlpha);
	ColorB colorGeometry(LOCALMEMORY_COLOR_GEOMETRY, sys_LocalMemoryDiagramAlpha);
	ColorB colorBlack(LOCALMEMORY_COLOR_BLACK);
	f32 fColorOK[4] = {LOCALMEMORY_FCOLOR_OK,1};
	f32 fColorWarning[4] = {LOCALMEMORY_FCOLOR_WARNING,1};
	f32 fColorError[4] = {LOCALMEMORY_FCOLOR_ERROR,1};
	f32 fColorOther[4] = {LOCALMEMORY_FCOLOR_OTHER,1};
	
	f32 *pColor;
	Vec3 v0, v1, v2, v3, v4, vTextureDiagram, vTextureDiagram2, vGeometryDiagram, vGeometryDiagram2;

	float localMemoryTextureLimit = sys_LocalMemoryTextureLimit  * 1024.f * 1024.f;
	float localMemoryGeometryLimit = sys_LocalMemoryGeometryLimit * 1024.f * 1024.f;

	for( int y=yMin; y<yMax; y++ )
	{
		sector = &m_arrSectors[xMin+y*LOCALMEMORY_SECTOR_NR_X];
		for( int x=xMin; x<xMax; x++ )
		{
			pos.x = (x + 0.5f) * LOCALMEMORY_SECTOR_SIZE;
			pos.y = (y + 0.5f) * LOCALMEMORY_SECTOR_SIZE;
			pos.z = gEnv->p3DEngine->GetTerrainElevation(pos.x,pos.y);

			if( m_pDebugDraw->GetIVal()==17 )
			{
				float textureRatio = sector->m_memoryUsage_Textures / localMemoryTextureLimit;
				float geometryRatio = sector->m_memoryUsage_Geometry / localMemoryGeometryLimit;
				float maxRatio = max( textureRatio, geometryRatio );

				v0 = pos;
				v1 = pos + Vec3(-sys_LocalMemoryObjectWidth,-sys_LocalMemoryObjectWidth,sys_LocalMemoryObjectHeight);
				v2 = pos + Vec3( sys_LocalMemoryObjectWidth,-sys_LocalMemoryObjectWidth,sys_LocalMemoryObjectHeight);
				v3 = pos + Vec3( sys_LocalMemoryObjectWidth, sys_LocalMemoryObjectWidth,sys_LocalMemoryObjectHeight);
				v4 = pos + Vec3(-sys_LocalMemoryObjectWidth, sys_LocalMemoryObjectWidth,sys_LocalMemoryObjectHeight);
				
				if( camera->Project(v1, projectedPos) || camera->Project(v2, projectedPos) || camera->Project(v3, projectedPos) || camera->Project(v4, projectedPos) )
				{
					vTextureDiagram  = pos + Vec3(0.f, sys_LocalMemoryObjectWidth+sys_LocalMemoryDiagramDistance+sys_LocalMemoryDiagramRadius,sys_LocalMemoryObjectHeight);
					vGeometryDiagram = pos + Vec3(0.f,-sys_LocalMemoryObjectWidth-sys_LocalMemoryDiagramDistance-sys_LocalMemoryDiagramRadius,sys_LocalMemoryObjectHeight);

					if( maxRatio <  sys_LocalMemoryWarningRatio) 
					{
						color = colorOK;
						pColor = fColorOK;
					}
					else if ( maxRatio <  1.0f ) 
					{
						color = colorWarning;
						pColor = fColorWarning;
					}
					else 
					{
						color = colorError;
						pColor = fColorError;
					}

					bool close = ( Distance::Point_Point2D(cameraPos, pos) < sys_LocalMemoryInnerViewDistance );
					if( close )
					{
						pRenderAuxGeom->SetRenderFlags(auxGeomRenderFlagsClose);

						static int xDebug = 14;
						static int yDebug = 19;

						if( sys_LocalMemoryDrawText )
						{
							camera->Project(pos, projectedPos);

							pRenderer->Draw2dLabel(projectedPos.x, projectedPos.y, 1.2f, pColor, true, "Text: %.2fMb Geom: %.2fMb", sector->m_memoryUsage_Textures/(1024.f*1024.f), sector->m_memoryUsage_Geometry/(1024.f*1024.f));
/* For sector debugging
							pRenderer->Draw2dLabel(projectedPos.x, projectedPos.y, 1.2f, pColor, true, "Text: %.1fMb Geom: %.1fMb - %d : %d", sector->m_memoryUsage_Textures/(1024.f*1024.f), sector->m_memoryUsage_Geometry/(1024.f*1024.f), x, y);

							if( xDebug == x && yDebug == y )
							{
								float sx = projectedPos.x-80;
								float sy = projectedPos.y;

								for( TStatObjMap::iterator it = m_globalStatObjs.begin(); it != m_globalStatObjs.end(); ++it )
								{
									SStatObjInfo *statObjInfo = &it->second;
									IStatObj* pStatObj = statObjInfo->m_pStatObj;

									int memoryUsage = 0;
									float mipFactor = statObjInfo->m_arrMipFactor[y * m_actProcessedSectors.w + x];
									if( mipFactor != BIG_NUMBER )
									{
										memoryUsage = statObjInfo->m_streamableContentMemoryUsage;
									}
									sy+=15.f;
									pRenderer->Draw2dLabel(sx, sy, 1.2f, pColor, false, "Geom: %.2fMb %.4f Act:%s Orig:%s %s", memoryUsage/(1024.f*1024.f), mipFactor, pStatObj->GetFilePath(), statObjInfo->m_filePath, statObjInfo->m_bSubObject ? " SUBOBJECT" : "");
									//pRenderer->Draw2dLabel(sx, sy, 1.2f, pColor, false, "Geom: %.2fMb %.4f", memoryUsage/(1024.f*1024.f), mipFactor);
								}
							}
//*/
						}
						if( sys_LocalMemoryLogText )
						{
							if( xDebug == x && yDebug == y )
							{
								for( TTextureMap::iterator it = m_globalTextures.begin(); it != m_globalTextures.end(); ++it )
								{
									STextureInfo *textureObjInfo = &it->second;
									ITexture *pTexture = textureObjInfo->m_pTexture;

									float mipFactor = textureObjInfo->m_arrMipFactor[y * m_actProcessedSectors.w + x];
									if( mipFactor != BIG_NUMBER )
									{
										CryLog("Texture: %s %dX%d mipfactor:%.4f", pTexture->GetName(), pTexture->GetWidth(), pTexture->GetHeight(), mipFactor);
									}
								}
								sys_LocalMemoryLogText = false;		//Log only once!
							}
						}
					}
					else
					{
						pRenderAuxGeom->SetRenderFlags(auxGeomRenderFlagsFar);
					}

					pRenderAuxGeom->DrawTriangle( v1, color, v2, color, v3, color );
					pRenderAuxGeom->DrawTriangle( v3, color, v4, color, v1, color );

					pRenderAuxGeom->DrawTriangle( v0, color, v1, color, v4, color );
					pRenderAuxGeom->DrawTriangle( v0, color, v2, color, v1, color );
					pRenderAuxGeom->DrawTriangle( v0, color, v3, color, v2, color );
					pRenderAuxGeom->DrawTriangle( v0, color, v4, color, v3, color );

					if( close )
					{
						LocalMemoryUsageDrawHelper::DrawCircle(pRenderer, camera, vTextureDiagram,  sys_LocalMemoryDiagramRadius, textureRatio,  colorTexture,  colorBlack);
						LocalMemoryUsageDrawHelper::DrawCircle(pRenderer, camera, vGeometryDiagram, sys_LocalMemoryDiagramRadius, geometryRatio, colorGeometry, colorBlack);
					}
				}
			}

			float localMemoryTextureStreamingSpeedLimit = sys_LocalMemoryTextureStreamingSpeedLimit  * 1024.f * 1024.f;
			float localMemoryStatObjStreamingSpeedLimit = sys_LocalMemoryGeometryStreamingSpeedLimit * 1024.f * 1024.f;
//*
			// StreamingSpeedLimit drawing: DIR X
			if( m_pDebugDraw->GetIVal()==18 )
			{
				float textureRatio1 = sector->m_memoryUsage_TexturesChangeXneg / localMemoryTextureStreamingSpeedLimit;
				float textureRatio2 = sector->m_memoryUsage_TexturesChangeXpos / localMemoryTextureStreamingSpeedLimit;
				float textureRatio = max(textureRatio1, textureRatio2);
				float geometryRatio1 = sector->m_memoryUsage_GeometryChangeXneg / localMemoryStatObjStreamingSpeedLimit;
				float geometryRatio2 = sector->m_memoryUsage_GeometryChangeXpos / localMemoryStatObjStreamingSpeedLimit;
				float geometryRatio = max(geometryRatio1, geometryRatio2);
				float maxRatio = max(textureRatio, geometryRatio);
				bool reverseTextureDir = textureRatio1 < textureRatio2;
				bool reverseGeometryDir = geometryRatio1 < geometryRatio2;

				if( maxRatio <  sys_LocalMemoryWarningRatio)
					color = colorOK;
				else if ( maxRatio <  1.0f ) 
					color = colorWarning;
				else 
					color = colorError;

				pos.x = (x + 0.0f) * LOCALMEMORY_SECTOR_SIZE;
				pos.y = (y + 0.5f) * LOCALMEMORY_SECTOR_SIZE;
				pos.z = gEnv->p3DEngine->GetTerrainElevation(pos.x,pos.y) + sys_LocalMemoryObjectHeight;

				Vec3 vDistance(0.f, sys_LocalMemoryDiagramStreamingSpeedDistance, 0.f);
				v1 = pos + Vec3( sys_LocalMemoryStreamingSpeedObjectLength, 0.f, 0.f );
				v2 = pos + Vec3(-sys_LocalMemoryStreamingSpeedObjectLength, 0.f, 0.f );
				v1.z = gEnv->p3DEngine->GetTerrainElevation(pos.x+LOCALMEMORY_SECTOR_SIZE*0.5f,pos.y) + sys_LocalMemoryObjectHeight;
				v2.z = gEnv->p3DEngine->GetTerrainElevation(pos.x-LOCALMEMORY_SECTOR_SIZE*0.5f,pos.y) + sys_LocalMemoryObjectHeight;
				float gradient = (v2.z-v1.z) / (v2.x-v1.x);

				bool close = Distance::Point_Point2D(cameraPos, pos) < sys_LocalMemoryInnerViewDistance;
				if( close )
				{
					pRenderAuxGeom->SetRenderFlags(auxGeomRenderFlagsClose);
				}
				else
				{
					pRenderAuxGeom->SetRenderFlags(auxGeomRenderFlagsFar);
				}

				if( textureRatio > 0.01f )
				{
					vTextureDiagram  = v1 + vDistance;
					vTextureDiagram2 = v2 + vDistance;
					LocalMemoryUsageDrawHelper::DrawArrow(pRenderer, camera, vTextureDiagram, vTextureDiagram2, 0.6f, 1.5f, sys_LocalMemoryDiagramStreamingSpeedRadius, color, (reverseTextureDir ? LocalMemoryUsageDrawHelper::DSP_REVERSE_ARROW : 0) );
					if( close )
					{
						LocalMemoryUsageDrawHelper::DrawCircle(pRenderer, camera, reverseTextureDir ? vTextureDiagram2 : vTextureDiagram,  sys_LocalMemoryDiagramStreamingSpeedRadius, textureRatio,  colorTexture,  colorBlack);
					}
				}

				if( geometryRatio > 0.01f )
				{
					vGeometryDiagram  = v1 - vDistance;
					vGeometryDiagram2 = v2 - vDistance;
					LocalMemoryUsageDrawHelper::DrawArrow(pRenderer, camera, vGeometryDiagram, vGeometryDiagram2, 0.6f, 1.5f, sys_LocalMemoryDiagramStreamingSpeedRadius, color, (reverseGeometryDir ? LocalMemoryUsageDrawHelper::DSP_REVERSE_ARROW : 0) );
					if( close )
					{
						LocalMemoryUsageDrawHelper::DrawCircle(pRenderer, camera, reverseGeometryDir ? vGeometryDiagram2 : vGeometryDiagram,  sys_LocalMemoryDiagramStreamingSpeedRadius, geometryRatio,  colorGeometry,  colorBlack);
					}
				}

				if( close )
				{
					if( sys_LocalMemoryDrawText )
					{
						float texturesChangeXmax = max(sector->m_memoryUsage_TexturesChangeXpos, sector->m_memoryUsage_TexturesChangeXneg)/(1024.f*1024.f);
						float geometryChangeXmax = max(sector->m_memoryUsage_GeometryChangeXpos, sector->m_memoryUsage_GeometryChangeXneg)/(1024.f*1024.f);
						int texturePiecesChangeXmax = max(sector->m_memoryUsage_TexturePiecesChangeXpos, sector->m_memoryUsage_TexturePiecesChangeXneg);
						int geometryPiecesChangeXmax = max(sector->m_memoryUsage_GeometryPiecesChangeXpos, sector->m_memoryUsage_GeometryPiecesChangeXneg);
						if( texturesChangeXmax>=0.01f || geometryChangeXmax>=0.01f || texturePiecesChangeXmax>0 || geometryPiecesChangeXmax>0)
						//if( texturePiecesChangeXmax>0)
						{
							bool visible = camera->Project(pos, projectedPos);
							if( visible )
							{
								pRenderer->Draw2dLabel(projectedPos.x, projectedPos.y, 1.2f, fColorOther, true, "TexDiffX:%.1fMb(%d) GeomDiffX:%.1fMb(%d)", 
																			 texturesChangeXmax, texturePiecesChangeXmax, geometryChangeXmax, geometryPiecesChangeXmax);
							}
						}
					}
				}
			}
//*/
//*
			// StreamingSpeedLimit drawing: DIR Y
			if( m_pDebugDraw->GetIVal()==18 )
			{
				float textureRatio1 = sector->m_memoryUsage_TexturesChangeYneg / localMemoryTextureStreamingSpeedLimit;
				float textureRatio2 = sector->m_memoryUsage_TexturesChangeYpos / localMemoryTextureStreamingSpeedLimit;
				float textureRatio = max(textureRatio1, textureRatio2);
				float geometryRatio1 = sector->m_memoryUsage_GeometryChangeYneg / localMemoryStatObjStreamingSpeedLimit;
				float geometryRatio2 = sector->m_memoryUsage_GeometryChangeYpos / localMemoryStatObjStreamingSpeedLimit;
				float geometryRatio = max(geometryRatio1, geometryRatio2);
				float maxRatio = max(textureRatio, geometryRatio);
				bool reverseTextureDir = textureRatio1 < textureRatio2;
				bool reverseGeometryDir = geometryRatio1 < geometryRatio2;

				if( maxRatio <  sys_LocalMemoryWarningRatio)
					color = colorOK;
				else if ( maxRatio <  1.0f ) 
					color = colorWarning;
				else 
					color = colorError;

				pos.x = (x + 0.5f) * LOCALMEMORY_SECTOR_SIZE;
				pos.y = (y + 0.0f) * LOCALMEMORY_SECTOR_SIZE;
				pos.z = gEnv->p3DEngine->GetTerrainElevation(pos.x,pos.y) + sys_LocalMemoryObjectHeight;

				Vec3 vDistance(sys_LocalMemoryDiagramStreamingSpeedDistance, 0.f, 0.f);
				v1 = pos + Vec3( 0.f, sys_LocalMemoryStreamingSpeedObjectLength, 0.f );
				v2 = pos + Vec3( 0.f,-sys_LocalMemoryStreamingSpeedObjectLength, 0.f );
				v1.z = gEnv->p3DEngine->GetTerrainElevation(pos.x,pos.y+LOCALMEMORY_SECTOR_SIZE*0.5f) + sys_LocalMemoryObjectHeight;
				v2.z = gEnv->p3DEngine->GetTerrainElevation(pos.x,pos.y-LOCALMEMORY_SECTOR_SIZE*0.5f) + sys_LocalMemoryObjectHeight;
				float gradient = (v2.z-v1.z) / (v2.y-v1.y);

				bool close = Distance::Point_Point2D(cameraPos, pos) < sys_LocalMemoryInnerViewDistance;
				if( close )
				{
					pRenderAuxGeom->SetRenderFlags(auxGeomRenderFlagsClose);
				}
				else
				{
					pRenderAuxGeom->SetRenderFlags(auxGeomRenderFlagsFar);
				}

				if( textureRatio > 0.01f )
				{
					vTextureDiagram  = v1 + vDistance;
					vTextureDiagram2 = v2 + vDistance;
					LocalMemoryUsageDrawHelper::DrawArrow(pRenderer, camera, vTextureDiagram, vTextureDiagram2, 0.6f, 1.5f, sys_LocalMemoryDiagramStreamingSpeedRadius, color, (reverseTextureDir ? LocalMemoryUsageDrawHelper::DSP_REVERSE_ARROW : 0) );
					if( close )
					{
						LocalMemoryUsageDrawHelper::DrawCircle(pRenderer, camera, reverseTextureDir ? vTextureDiagram2 : vTextureDiagram,  sys_LocalMemoryDiagramStreamingSpeedRadius, textureRatio,  colorTexture,  colorBlack);
					}
				}

				if( geometryRatio > 0.01f )
				{
					vGeometryDiagram  = v1 - vDistance;
					vGeometryDiagram2 = v2 - vDistance;
					LocalMemoryUsageDrawHelper::DrawArrow(pRenderer, camera, vGeometryDiagram, vGeometryDiagram2, 0.6f, 1.5f, sys_LocalMemoryDiagramStreamingSpeedRadius, color, (reverseGeometryDir ? LocalMemoryUsageDrawHelper::DSP_REVERSE_ARROW : 0) );
					if( close )
					{
						LocalMemoryUsageDrawHelper::DrawCircle(pRenderer, camera, reverseGeometryDir ? vGeometryDiagram2 : vGeometryDiagram,  sys_LocalMemoryDiagramStreamingSpeedRadius, geometryRatio,  colorGeometry,  colorBlack);
					}
				}

				if( close )
				{
					if( sys_LocalMemoryDrawText )
					{
						float texturesChangeYmax = max(sector->m_memoryUsage_TexturesChangeYpos, sector->m_memoryUsage_TexturesChangeYneg)/(1024.f*1024.f);
						float geometryChangeYmax = max(sector->m_memoryUsage_GeometryChangeYpos, sector->m_memoryUsage_GeometryChangeYneg)/(1024.f*1024.f);
						int texturePiecesChangeYmax = max(sector->m_memoryUsage_TexturePiecesChangeYpos, sector->m_memoryUsage_TexturePiecesChangeYneg);
						int geometryPiecesChangeYmax = max(sector->m_memoryUsage_GeometryPiecesChangeYpos, sector->m_memoryUsage_GeometryPiecesChangeYneg);
						if( texturesChangeYmax>=0.01f || geometryChangeYmax>=0.01f || texturePiecesChangeYmax>0 || geometryPiecesChangeYmax>0)
//						if( texturePiecesChangeYmax>0)
						{
							bool visible = camera->Project(pos, projectedPos);
							if( visible )
							{
								pRenderer->Draw2dLabel(projectedPos.x, projectedPos.y, 1.2f, fColorOther, true, "TexDiffY:%.1f(%d) GeomDiffY:%.1f(%d)", 
																			 texturesChangeYmax, texturePiecesChangeYmax, geometryChangeYmax, geometryPiecesChangeYmax);
							}
						}
					}
				}
			}
//*/

			sector++;
		}
	}
}

void CLocalMemoryUsage::OnUpdate()
{
	if( !m_pDebugDraw || (m_pDebugDraw->GetIVal()!=17 && m_pDebugDraw->GetIVal()!=18) )
		return;

	//CryLogAlways("OnUpdate start-----------------------------------------");
	CTimeValue actTime = gEnv->pTimer->GetAsyncTime();

	float callTimeDiff = (actTime - m_LastCallTime).GetSeconds();
	float neededCallTimeDiff = m_AverageUpdateTime / (sys_LocalMemoryOptimalMSecPerSec / 1000.f);

	if( callTimeDiff < neededCallTimeDiff && callTimeDiff < sys_LocalMemoryMaxMSecBetweenCalls / 1000.f)
		return;

	DeleteUnusedResources();

	m_LastCallTime = actTime;

	{
		FRAME_PROFILER("! LocalMemoryUsege::Update - Start", GetISystem(), PROFILE_SYSTEM);
		//StartChecking(RectI(0,0,m_sectorNr.x, m_sectorNr.y));
		StartChecking(m_actDrawedSectors);
	}

	{
		FRAME_PROFILER("! LocalMemoryUsege::Update - CollectGeometry P1", GetISystem(), PROFILE_SYSTEM);
		CollectGeometryP1();
	}
	{
		ITerrain *pTerrain = gEnv->p3DEngine->GetITerrain();

		if( pTerrain )
		{
			TMaterialMap m_detailTerrainTextureMaterials;

			int materialNumber = pTerrain->GetDetailTextureMaterials(NULL);
			PodArray<IMaterial*> materials(materialNumber, materialNumber);

			pTerrain->GetDetailTextureMaterials(&materials[0]);

			AABB bigAABB( 1e8f );

			for( int i=0; i < materialNumber; i++ )
			{
				IMaterial *pMaterial = materials[i];

				CheckMeshMaterialP1( NULL, pMaterial, bigAABB, 1e8f, 1.0f );
			}
		}
	}

	{
		FRAME_PROFILER("! LocalMemoryUsege::Update - Materials P2", GetISystem(), PROFILE_SYSTEM);
		//We must iterate on materials first
		for( TMaterialMap::iterator it = m_globalMaterials.begin(); it != m_globalMaterials.end(); ++it )
		{
			it->second.CheckOnAllSectorsP2();
		}
	}
	{
		FRAME_PROFILER("! LocalMemoryUsege::Update - Textures P2", GetISystem(), PROFILE_SYSTEM);
		for( TTextureMap::iterator it = m_globalTextures.begin(); it != m_globalTextures.end(); ++it )
		{
			it->second.CheckOnAllSectorsP2();
		}
	}
	{
		FRAME_PROFILER("! LocalMemoryUsege::Update - StatObjects P2", GetISystem(), PROFILE_SYSTEM);
		for( TStatObjMap::iterator it = m_globalStatObjs.begin(); it != m_globalStatObjs.end(); ++it )
		{
			it->second.CheckOnAllSectorsP2();
		}
	}

	CTimeValue endTime = gEnv->pTimer->GetAsyncTime();
	m_AverageUpdateTime = LERP(m_AverageUpdateTime, (endTime - actTime).GetSeconds(), 0.5f);

	//CryLogAlways("OnUpdate end***");
}

void CLocalMemoryUsage::DeleteUnusedResources()
{
	FRAME_PROFILER("! LocalMemoryUsege::Update - Deleting unused resources", GetISystem(), PROFILE_SYSTEM);

	// Clear the used flags
	for( TStatObjMap::iterator it = m_globalStatObjs.begin(); it != m_globalStatObjs.end(); ++it )
		it->second.m_used = false;

	// Iterate on used StatObjs
	int nCount;
	gEnv->p3DEngine->GetLoadedStatObjArray(NULL, nCount);
	std::vector<IStatObj*>	objectsArray;
	objectsArray.resize(nCount);
	if( nCount > 0 )
	{
		gEnv->p3DEngine->GetLoadedStatObjArray(&objectsArray[0], nCount);
	}

	for( std::vector<IStatObj*>::iterator it = objectsArray.begin(); it != objectsArray.end(); ++it )
	{
		IStatObj *pStatObj = *it;
		TStatObjMap::iterator findIt = m_globalStatObjs.find((INT_PTR)pStatObj);

		if( findIt != m_globalStatObjs.end() && strcmp(pStatObj->GetFilePath(), findIt->second.m_filePath) == 0 && !findIt->second.m_bSubObject)
		{
			findIt->second.m_used = true;
		}

		if (pStatObj->GetFlags() & STATIC_OBJECT_COMPOUND)
		{
			for (int k = 0; k < pStatObj->GetSubObjectCount(); k++)
			{
				if (!pStatObj->GetSubObject(k))
					continue;

				IStatObj *pSubStatObj = pStatObj->GetSubObject(k)->pStatObj;

				if(pSubStatObj)
				{
					findIt = m_globalStatObjs.find((INT_PTR)pSubStatObj);

					if( findIt != m_globalStatObjs.end() && strcmp(pSubStatObj->GetFilePath(), findIt->second.m_filePath) == 0 && findIt->second.m_bSubObject)
					{
						findIt->second.m_used = true;
					}
				}
			}
		}
	}

	// Erase non-used StatObjs
	bool changed = true;
	while( changed )
	{
		changed = false;
		for( TStatObjMap::iterator it = m_globalStatObjs.begin(); it != m_globalStatObjs.end(); ++it )
		{
			if( !it->second.m_used )
			{
				m_globalStatObjs.erase(it);
				changed = true;
				break;
			}
		}
	}
/*
/*
	for( TTextureMap::iterator it = m_globalTextures.begin(); it != m_globalTextures.end(); ++it )
		it->second.m_used = false;

	bool changed = true;
	while( changed )
	{
		changed = false;
		for( TTextureMap::iterator it = m_globalTextures.begin(); it != m_globalTextures.end(); ++it )
		{
			if( !it->second.m_used )
			{
				m_globalTextures.erase(it);
				changed = true;
				break;
			}
		}
	}
	
	for( TMaterialMap::iterator it = m_globalMaterials.begin(); it != m_globalMaterials.end(); ++it )
		it->second.m_used = false;

	changed = true;
	while( changed )
	{
		changed = false;
		for( TMaterialMap::iterator it = m_globalMaterials.begin(); it != m_globalMaterials.end(); ++it )
		{
			if( !it->second.m_used )
			{
				m_globalMaterials.erase(it);
				changed = true;
				break;
			}
		}
	}
//*/
}
void CLocalMemoryUsage::StartChecking(const RectI &actProcessedSectors)
{
	m_actProcessedSectors = actProcessedSectors;

	for( int y=0; y<m_sectorNr.y; y++ )
	{
		SSector *sector = &m_arrSectors[y*LOCALMEMORY_SECTOR_NR_X];
		for( int x=0; x<m_sectorNr.x; x++ )
		{
			sector->StartChecking();
			sector++;
		}
	}

	{
		for( TTextureMap::iterator it = m_globalTextures.begin(); it != m_globalTextures.end(); ++it )
		{
			//FRAME_PROFILER("! LocalMemoryUsege::StartChecking - Textures", GetISystem(), PROFILE_SYSTEM);
			it->second.StartChecking();
		}
	}
	{
		for( TMaterialMap::iterator it = m_globalMaterials.begin(); it != m_globalMaterials.end(); ++it )
		{
			//FRAME_PROFILER("! LocalMemoryUsege::StartChecking - Materials", GetISystem(), PROFILE_SYSTEM);
			it->second.StartChecking();
		}
	}
	{
		for( TStatObjMap::iterator it = m_globalStatObjs.begin(); it != m_globalStatObjs.end(); ++it )
		{
			//FRAME_PROFILER("! LocalMemoryUsege::StartChecking - StatObjects", GetISystem(), PROFILE_SYSTEM);
			it->second.StartChecking();
		}
	}
}

void CLocalMemoryUsage::CollectGeometryP1()
{
	ISystem *pSystem = GetISystem();
	I3DEngine *p3DEngine = pSystem->GetI3DEngine();

	// iterate through all instances
	std::vector<IRenderNode*>	renderNodes;

	uint32 dwCount=0;

	dwCount+=p3DEngine->GetObjectsByType(eERType_Light);
	
	dwCount+=p3DEngine->GetObjectsByType(eERType_RenderProxy);
	dwCount+=p3DEngine->GetObjectsByType(eERType_Brush);
	dwCount+=p3DEngine->GetObjectsByType(eERType_Vegetation);
	dwCount+=p3DEngine->GetObjectsByType(eERType_Decal);
	dwCount+=p3DEngine->GetObjectsByType(eERType_Road);

	if(dwCount > 0)
	{
		renderNodes.resize(dwCount+1);
		dwCount=0;

		dwCount+=p3DEngine->GetObjectsByType(eERType_Light,&renderNodes[dwCount]);
		dwCount+=p3DEngine->GetObjectsByType(eERType_RenderProxy,&renderNodes[dwCount]);
		dwCount+=p3DEngine->GetObjectsByType(eERType_Brush,&renderNodes[dwCount]);
		dwCount+=p3DEngine->GetObjectsByType(eERType_Vegetation,&renderNodes[dwCount]);
		dwCount+=p3DEngine->GetObjectsByType(eERType_Decal,&renderNodes[dwCount]);
		dwCount+=p3DEngine->GetObjectsByType(eERType_Road,&renderNodes[dwCount]);

		AABB objBox;

		for(uint32 dwI=0; dwI<dwCount; ++dwI)
		{
			IRenderNode *pRenderNode = renderNodes[dwI];

			if(pRenderNode->m_fWSMaxViewDist < 0.01f)
			{
				continue;
			}

			if(pRenderNode->GetRndFlags() & ERF_PROCEDURAL)
			{
				continue;																						//Procedural generated vegetation. TODO we must calculate it by other way
			}

			float objScale = 1.f;
			if(pRenderNode->GetRenderNodeType() == eERType_Decal)
			{
				IDecalRenderNode * pDecal = (IDecalRenderNode *)pRenderNode;
				objScale = max(0.001f,pDecal->GetMatrix().GetColumn0().GetLength());
			}
			else if(pRenderNode->GetRenderNodeType() == eERType_Vegetation)
			{
				IVegetation * pVeg = (IVegetation *)pRenderNode;
				objScale = max(0.001f,pVeg->GetScale());
			}
			else if(pRenderNode->GetRenderNodeType() == eERType_Brush)
			{
				objScale = max(0.001f,((IBrush *)pRenderNode)->GetMatrix().GetColumn0().GetLength());
			}
			else if(pRenderNode->GetRenderNodeType() == eERType_FogVolume)
			{
				objScale = max(0.001f,((IFogVolumeRenderNode *)pRenderNode)->GetMatrix().GetColumn0().GetLength());
			}

			float maxViewDist = pRenderNode->m_fWSMaxViewDist;
			pRenderNode->FillBBox(objBox);

			IMaterial* pRenderNodeMat = pRenderNode->GetMaterialOverride();
			
			{
				IStatObj *pStatObj = pRenderNode->GetEntityStatObj();

				if (pStatObj != NULL)
				{
					CheckStatObjMaterialP1(pStatObj, pRenderNodeMat, objBox, maxViewDist, objScale);
				}
				else
				{
					CheckMeshMaterialP1(pRenderNode->GetRenderMesh(0), pRenderNodeMat, objBox, maxViewDist, objScale);
				}
			}

			for(int dwSlot=0; dwSlot<pRenderNode->GetSlotCount(); ++dwSlot)
			{
				IMaterial* pSlotMat = pRenderNode->GetEntitySlotMaterial(dwSlot);
				if(!pSlotMat)
					pSlotMat = pRenderNodeMat;

				Matrix34A matParent;

				if(IStatObj *pStatObj = pRenderNode->GetEntityStatObj(dwSlot, 0, &matParent, true))
				{
					CheckStatObjP1(pStatObj, pRenderNode, objBox, maxViewDist, objScale);

					IMaterial* pStatObjMat = pStatObj->GetMaterial();
					CheckStatObjMaterialP1(pStatObj, pSlotMat ? pSlotMat : pStatObjMat, objBox, maxViewDist, objScale);
				}

				if(ICharacterInstance * pCharacter = pRenderNode->GetEntityCharacter(dwSlot, &matParent, true))
				{
					CheckCharacterP1( pCharacter, pRenderNode, pSlotMat, objBox, maxViewDist, objScale, 2);
				}
			}
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CLocalMemoryUsage::SStatObjInfo *CLocalMemoryUsage::CheckStatObjP1( IStatObj *pStatObj, IRenderNode *pRenderNode, AABB bounding, float maxViewDist, float scale )
{
	if( !pStatObj )
		return NULL;

	if( strncmp(pStatObj->GetFilePath(), "%level%", 7)==0 )			//HACK %LEVEL% brushes
		return NULL;

	SStatObjInfo *pStatObjInfo = NULL;

	TStatObjMap::iterator iter = m_globalStatObjs.find((INT_PTR)pStatObj);
	if( iter!=m_globalStatObjs.end() )
	{
		pStatObjInfo = &iter->second;
		pStatObjInfo->m_streamableContentMemoryUsage = pStatObj->GetStreamableContentMemoryUsage();	//TODO TEMP
	}
	else
	{
		{
			FRAME_PROFILER("! CLocalMemoryUsage::CheckStatObjP1 HASH", GetISystem(), PROFILE_SYSTEM);
			pStatObjInfo = &m_globalStatObjs[(INT_PTR)pStatObj];
		}

		pStatObjInfo->Init(this);
		pStatObjInfo->m_streamableContentMemoryUsage = pStatObj->GetStreamableContentMemoryUsage();
		//pStatObjInfo->m_pStatObj = pStatObj;
		pStatObjInfo->m_filePath = pStatObj->GetFilePath();
		pStatObjInfo->m_bSubObject = pStatObj->IsSubObject();
		//CollectStatObjInfo_Recursive( pStatObjInfo, pStatObj );
	}

	pStatObjInfo->CheckOnAllSectorsP1(bounding, maxViewDist, scale, 1.0f);

	return pStatObjInfo;
}
/*
void CLocalMemoryUsage::CollectStatObjInfo_Recursive( SStatObjInfo* statObjInfo, IStatObj *pStatObj )
{
//	We collected this material previously
//	if(pStatObj->GetMaterial())
//	{
//		statObjInfo->AddMaterialInfo( CheckMaterialP1(pStatObj->GetMaterial()) );
//	}

	bool bMultiSubObj = (pStatObj->GetFlags() & STATIC_OBJECT_COMPOUND) != 0;

	if (!bMultiSubObj)
	{
		CollectGeometryInfo( statObjInfo, pStatObj );
	}
	else
	{
		for (int k = 0; k < pStatObj->GetSubObjectCount(); k++)
		{
			if (!pStatObj->GetSubObject(k))
				continue;

			IStatObj *pSubStatObj = pStatObj->GetSubObject(k)->pStatObj;

			if(pSubStatObj)
			{
				CollectStatObjInfo_Recursive( statObjInfo, pSubStatObj );	//(Spidy) Must be recursive
			}
		}
	}
}
void CLocalMemoryUsage::CollectGeometryInfo( SStatObjInfo* statObjInfo, IStatObj *pStatObj )
{
	//////////////////////////////////////////////////////////////////////////
	// Iterate LODs.
	//////////////////////////////////////////////////////////////////////////

	IStatObj *pLod0 = 0;
	for (int lod = 0; lod < MAX_LODS; lod++)
	{
		IStatObj *pLod = pStatObj->GetLodObject(lod);
		if (pLod)
		{
			if (!pLod0 && pLod->GetRenderMesh())
				pLod0 = pLod;

			statObjInfo->m_lodNr = max(statObjInfo->m_lodNr, lod);			// Assign last existing lod.

			if (pLod->GetRenderMesh())
			{
				statObjInfo->m_indicesPerLod[lod] += pLod->GetRenderMesh()->GetSysIndicesCount();
				statObjInfo->m_meshSize += pLod->GetRenderMesh()->GetMemoryUsage(0,IRenderMesh::MEM_USAGE_COMBINED);

				//IMaterial *pMaterial = pLod->GetMaterial();
				//statObjInfo->AddMaterialInfo( CheckMaterialP1( pMaterial ) );	//(Spidy) We must use all textures in material, not only the rendered ones!
			}
		}
	}

	if (pLod0 && pLod0->GetRenderMesh())
	{
		IRenderMesh *pRenderMesh = pLod0->GetRenderMesh();
		statObjInfo->m_vertices += pRenderMesh->GetVertCount();
		statObjInfo->m_indices += pRenderMesh->GetSysIndicesCount();
	}

	for(int j = 0; j < 4; j++)
	{
		if (pStatObj->GetPhysGeom(j))
		{
			CrySizerImpl physMeshSizer;
			pStatObj->GetPhysGeom(j)->pGeom->GetMemoryStatistics(&physMeshSizer);
			statObjInfo->m_physProxySize += physMeshSizer.GetTotalSize(); 
			statObjInfo->m_physPrimitives += pStatObj->GetPhysGeom(j)->pGeom->GetPrimitiveCount();
		}
	}
}
*/

void CLocalMemoryUsage::CheckCharacterP1( ICharacterInstance *pCharacter, IRenderNode *pRenderNode, IMaterial* pSlotMat, AABB bounding, float maxViewDist, float scale, int nMaxDepth )
{
  if(!nMaxDepth || !pCharacter)
    return;
  nMaxDepth--;

	if(IAttachmentManager * pAttMan = pCharacter->GetIAttachmentManager())
  {
		if(ICharacterInstance *pCharInstance = pAttMan->GetSkinInstance())
		{
			if(pCharInstance != pCharacter)
				CheckCharacterP1( pCharacter, pRenderNode, pSlotMat, bounding, maxViewDist, scale, nMaxDepth );
		}
		if(ICharacterInstance *pCharInstance = pAttMan->GetSkelInstance())
		{
			if(pCharInstance != pCharacter)
				CheckCharacterP1( pCharacter, pRenderNode, pSlotMat, bounding, maxViewDist, scale, nMaxDepth );
		}

    int nCount = pAttMan->GetAttachmentCount();
    for(int i=0; i<nCount; i++)
    {
      if(IAttachment * pAtt = pAttMan->GetInterfaceByIndex(i))
      {
        if(IAttachmentObject * pAttObj = pAtt->GetIAttachmentObject())
        {
					IMaterial* pAttMatOverride = pAttObj->GetMaterialOverride();
					IMaterial* pAttMat = pSlotMat ? pSlotMat : (pAttMatOverride ? pAttMatOverride : pAttObj->GetMaterial());

          if(ICharacterInstance *pCharInstance = pAttObj->GetICharacterInstance())
						CheckCharacterP1( pCharacter, pRenderNode, pAttMat, bounding, maxViewDist, scale, nMaxDepth );

          if(IStatObj * pStatObj = pAttObj->GetIStatObj())
          {
            if(!pStatObj || pStatObj->GetFlags()&STATIC_OBJECT_HIDDEN)
              continue;

						CheckStatObjP1( pStatObj, pRenderNode, bounding, maxViewDist, scale );

						IMaterial* pStatObjMat = pStatObj->GetMaterial();
						CheckStatObjMaterialP1(pStatObj, pAttMat ? pAttMat : pStatObjMat, bounding, maxViewDist, scale);
          }
        }
      }
    }

		IMaterial* pCharObjMat = pCharacter->GetMaterial();

		CheckMeshMaterialP1(pCharacter->GetRenderMesh(),pSlotMat ? pSlotMat : pCharObjMat, bounding, maxViewDist, scale);
  }

	// joints
  if(ISkeletonPose * pSkeletonPose = pCharacter->GetISkeletonPose())
	{
		uint32 numJoints = pSkeletonPose->GetJointCount();

		// check StatObj attachments
		for(uint32 i=0; i<numJoints; i++)
		{
			IStatObj * pStatObj = pSkeletonPose->GetStatObjOnJoint(i);
			if(!pStatObj || pStatObj->GetFlags()&STATIC_OBJECT_HIDDEN)
				continue;

			CheckStatObjP1( pStatObj, pRenderNode, bounding, maxViewDist, scale );

			IMaterial* pStatObjMat = pStatObj->GetMaterial();

			CheckStatObjMaterialP1(pStatObj, pSlotMat ? pSlotMat : pStatObjMat, bounding, maxViewDist, scale);
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void CLocalMemoryUsage::CheckMaterialP1( IMaterial *pMaterial, AABB bounding, float maxViewDist, float scale, float mipFactor )
{
	if (pMaterial)
	{
		SMaterialInfo *pMaterialInfo = NULL;

		TMaterialMap::iterator iter = m_globalMaterials.find((INT_PTR)pMaterial);
		if( iter!=m_globalMaterials.end() )
		{
			pMaterialInfo = &iter->second;
		}
		else
		{
			{
				FRAME_PROFILER("! CLocalMemoryUsage::CheckMaterialP1 HASH", GetISystem(), PROFILE_SYSTEM);
				pMaterialInfo = &m_globalMaterials[(INT_PTR)pMaterial];
			}
			pMaterialInfo->Init(this);
			//pMaterialInfo->m_pMaterial=pMaterial;
			CollectMaterialInfo_Recursive( pMaterialInfo, pMaterial );
		}

		pMaterialInfo->CheckOnAllSectorsP1(bounding, maxViewDist, scale, mipFactor);
	}
}

void CLocalMemoryUsage::CheckChunkMaterialP1( IMaterial *pMaterial, AABB bounding, float maxViewDist, float scale, CRenderChunk *pRenderChunk )
{
	IMaterial *pCurrentMaterial = pMaterial;

	if (pRenderChunk != NULL)
	{
		if (pRenderChunk->m_nMatID < pMaterial->GetSubMtlCount())
		{
			pCurrentMaterial = pMaterial->GetSubMtl(pRenderChunk->m_nMatID);
		}

		if (pCurrentMaterial != NULL)
		{
			CheckMaterialP1(pCurrentMaterial,bounding,maxViewDist,scale,pRenderChunk->m_texelAreaDensity);
		}
	}
	else
	{
		CheckMaterialP1(pCurrentMaterial,bounding,maxViewDist,scale,1.f);
	}
}

void CLocalMemoryUsage::CheckMeshMaterialP1( IRenderMesh *pRenderMesh, IMaterial *pMaterial, AABB bounding, float maxViewDist, float scale )
{
	if (pRenderMesh)
	{
		if (pMaterial)
		{
			PodArray<CRenderChunk> *pChunks = &pRenderMesh->GetChunks();

			if (pChunks != NULL)
			{
				for (unsigned int i = 0; i < pChunks->Size(); i++)
				{
					CheckChunkMaterialP1(pMaterial,bounding,maxViewDist,scale,&(*pChunks)[i]);
				}
			}

			pChunks = pRenderMesh->GetChunksSkinned();

			if (pChunks != NULL)
			{
				for (unsigned int i = 0; i < pChunks->Size(); i++)
				{
					CheckChunkMaterialP1(pMaterial,bounding,maxViewDist,scale,&(*pChunks)[i]);
				}
			}
		}
	}
	else
	{
		if (pMaterial)
		{
			CheckChunkMaterialP1(pMaterial,bounding,maxViewDist,scale,NULL);
		}
	}
}

void CLocalMemoryUsage::CheckStatObjMaterialP1( IStatObj *pStatObj, IMaterial *pMaterial, AABB bounding, float maxViewDist, float scale )
{
	if (pStatObj)
	{
		for (int i = 0; i < pStatObj->GetSubObjectCount(); i++)
		{
			CheckStatObjMaterialP1(pStatObj->GetSubObject(i)->pStatObj,pMaterial,bounding,scale,maxViewDist);
		}

		CheckMeshMaterialP1(pStatObj->GetRenderMesh(),pMaterial,bounding,maxViewDist,scale);
	}
}

void CLocalMemoryUsage::CollectMaterialInfo_Recursive( SMaterialInfo* materialInfo, IMaterial *pMaterial )
{
	SShaderItem &rItem = pMaterial->GetShaderItem();

	uint32 dwSubMatCount = pMaterial->GetSubMtlCount();

	for(uint32 dwSubMat=0;dwSubMat<dwSubMatCount;++dwSubMat)
	{
		IMaterial *pSub = pMaterial->GetSubMtl(dwSubMat);

		if(pSub)
			CollectMaterialInfo_Recursive(materialInfo, pSub);
	}

	// this pMaterial
	if(rItem.m_pShaderResources)
	{
		for(uint32 dwI=0;dwI<EFTT_MAX;++dwI)
		{
			SEfResTexture *pTex = rItem.m_pShaderResources->GetTexture(dwI);

			if(pTex && pTex->m_Sampler.m_pITex)
			{
				ITexture *pTexture = pTex->m_Sampler.m_pITex;
				if( pTexture && pTexture->GetStreamableMipNumber()>0)
				{
					STextureInfoAndTilingFactor textureInfo;

					textureInfo.m_pTextureInfo = GetTextureInfo(pTexture);
					textureInfo.m_tilingFactor = pTex->GetTiling(0) * pTex->GetTiling(1);

					materialInfo->AddTextureInfo(textureInfo);
				}
			}
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CLocalMemoryUsage::STextureInfo *CLocalMemoryUsage::GetTextureInfo( ITexture *pTexture )
{
	if( !pTexture )
		return NULL;

	TTextureMap::iterator iter = m_globalTextures.find((INT_PTR)pTexture);
	if( iter!=m_globalTextures.end() )
	{
		return &iter->second;
	}

	//FUNCTION_PROFILER(GetISystem(), PROFILE_SYSTEM);
	STextureInfo *pTextureInfo;
	{
		FRAME_PROFILER("! CLocalMemoryUsage::CheckStatObjP1 HASH", GetISystem(), PROFILE_SYSTEM);
		pTextureInfo = &m_globalTextures[(INT_PTR)pTexture];
	}
	pTextureInfo->Init(this);
	pTextureInfo->m_pTexture = pTexture;
	pTexture->AddRef();

	//Collect informations
	//pTextureInfo->m_size = pTexture->GetDeviceDataSize();
	//pTextureInfo->m_xSize = pTexture->GetWidth();
	//pTextureInfo->m_ySize = pTexture->GetHeight();
	//pTextureInfo->m_numMips = pTexture->GetNumMips();

	return pTextureInfo;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif //PS3 && XENON